package uicgas.pump.device.impl;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import org.apache.log4j.Logger;

import uicgas.pump.PumpLogic;
import uicgas.pump.PumpStateChangeNotifiable;
import uicgas.pump.device.PhysicalPump;
import uicgas.tools.LogHelper;

public class MockPump implements PhysicalPump
{
	private static final Logger LOGGER = LogHelper.getLogger(MockPump.class);

	private static enum PumpState
	{
		ACTIVE, DEACTIVE, PUMPING
	}

	private Set<PumpStateChangeNotifiable> changeNotifiableSet = new HashSet<PumpStateChangeNotifiable>();
	private PumpLogic pumpLogic;

	private Random random = new Random();

	private static final int SMALLEST_AMOUNT_OF_PUMP_TIME = 10;
	private static final int LARGEST_AMOUNT_OF_PUMP_TIME = 30;

	private double ammountPumpedDollars = 0;
	private double ammountPumpedGallons = 0;

	private GasolineGrade grade = GasolineGrade.Regular;

	private PumpState state = PumpState.DEACTIVE;

	public MockPump(PumpStateChangeNotifiable changeNotifiable,
			PumpLogic pumpLogic)
	{
		addPumpStateChangeListener(changeNotifiable);
		this.pumpLogic = pumpLogic;
	}

	public void addPumpStateChangeListener(
			PumpStateChangeNotifiable changeNotifiable)
	{
		if (changeNotifiable != null)
		{
			changeNotifiableSet.add(changeNotifiable);
		}
	}

	@Override
	public void changeGrade(GasolineGrade grade)
	{
		this.grade = grade;
	}

	@Override
	public void startPumping()
	{
		state = PumpState.PUMPING;

		int gasAmountToPump = generateTimesInSecond();
		for (int i = 1; i < gasAmountToPump + 1; i++)
		{
			synchronized (this)
			{
				if (state != PumpState.PUMPING)
				{
					return;
				}
			}

			ammountPumpedGallons = i;
			ammountPumpedDollars = ammountPumpedGallons * grade.getPrice();

			for (PumpStateChangeNotifiable changeNotifiable : changeNotifiableSet)
			{
				changeNotifiable.ammountChanged(grade, ammountPumpedGallons,
						ammountPumpedDollars);
			}

			try
			{
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
				// ignore
			}
		}

		pumpLogic.finishPumping();
		//		deactivePump();
	}

	private int generateTimesInSecond()
	{
		int num = Math.abs(random.nextInt() % LARGEST_AMOUNT_OF_PUMP_TIME);
		while (num < SMALLEST_AMOUNT_OF_PUMP_TIME)
		{
			num = Math.abs(random.nextInt() % LARGEST_AMOUNT_OF_PUMP_TIME);
		}

		return Math.abs(num);
	}

	@Override
	public void finishedPumping()
	{
		state = PumpState.DEACTIVE;

		for (PumpStateChangeNotifiable changeNotifiable : changeNotifiableSet)
		{
			changeNotifiable.finishedPumping(grade, ammountPumpedGallons,
					ammountPumpedDollars);
		}
	}

	@Override
	public void activePump()
	{
		ammountPumpedDollars = 0;
		ammountPumpedGallons = 0;

		state = PumpState.ACTIVE;

		for (PumpStateChangeNotifiable changeNotifiable : changeNotifiableSet)
		{
			changeNotifiable.readyForPumping();
		}

		LOGGER.debug("Pump is actived. ");
	}

	@Override
	public double getPumpedDollars()
	{
		return ammountPumpedDollars;
	}

	@Override
	public double getPumpedGallons()
	{
		return ammountPumpedGallons;
	}

	@Override
	public void deactivePump()
	{
		state = PumpState.DEACTIVE;
		grade = GasolineGrade.Regular;

		LOGGER.debug("Pump is deactived. ");
		//		changeNotifiable.finishedPumping(grade, ammountPumpedGallons,
		//				ammountPumpedDollars);
	}

	public GasolineGrade getGrade()
	{
		return grade;
	}
}
